﻿using Nini.Config;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using UltimateMusicTagger;
using UltimateMusicTagger.Model;

namespace UltimateMp3TaggerUI
{
    public partial class Form1 : Form
    {
        private UltimateMusicTagger.UltiMp3Tagger umtTagger;

        private UltimateMusicTagger.Business.MusicBrainzUtility musicBrainzUtility;

        private UltimateMusicTagger.Business.LastfmUtility lastfmUtility;

        private WebProxy proxy = null;

        private readonly Color ColorHighlight = Color.FromArgb(0x3399FF);
        private readonly Color ColorFontHighlight = Color.Gray;
        private readonly Color ColorMarkFile = Color.OrangeRed;

        const int ColCheckbox = 0;
        const int ColPosition = 1;
        const int ColName = 2;

        [Flags]
        private enum ARTIST_ITEMS_SEARCH 
        { 
            NONE = 0x00,
            TOP_ALBUMS = 0x01, 
            TOP_TRACKS = 0x02
        };

        private enum TAG_LIST_MODE
        {
            SELECTION,
            NAME,
            POSITION
        }

        private enum DB_SITE
        {
            LASTFM,
            MUSICBRAINZ
        }

        public Form1()
        {
            InitializeComponent();

            Init();

            InitGui();
        }

        enum MESSAGE_TYPE {INFO, ERROR, WARNING}

        private void ShowMessage(string message, MESSAGE_TYPE type)
        {
            this.labelSuggestedStep.Text = message;
            
            Color defaultColor = Color.Black;
            Color currentColor = Color.Black;
            Color targetColor = defaultColor;

            switch (type)
            {
                case MESSAGE_TYPE.INFO:
                    targetColor = Color.Green;
                    break;
                case MESSAGE_TYPE.WARNING:
                    targetColor = Color.Olive;
                    break;
                case MESSAGE_TYPE.ERROR:
                    targetColor = Color.Red;
                    break;
                //default:
                //    color = Color.Black;
            }

            this.labelSuggestedStep.ForeColor = targetColor;

            int ticks = 0;

            bool visible = false;

            //Timer timer = new Timer();
            //timer.Interval = 300;
            //timer.Tick += (obj, args) => {

            //    //currentColor = currentColor == defaultColor ? targetColor : defaultColor;
            //    //this.labelSuggestedStep.ForeColor = currentColor;
            //    this.pictureBoxInfoAlert.Visible = visible = !visible;
            //    if (ticks++ == 9)
            //        timer.Stop();
            //};
            //timer.Start();
        }

        void Form1_Load(object sender, EventArgs e)
        {
            UMTUtility.ShowUpdateSoftwareMessageAsync(false, proxy);
        }

        private void Init()
        {
            this.proxy = UMTUtility.GetProxyFromConfig();

            umtTagger = new UltimateMusicTagger.UltiMp3Tagger(null, proxy);

            musicBrainzUtility = new UltimateMusicTagger.Business.MusicBrainzUtility(proxy);

            lastfmUtility = new UltimateMusicTagger.Business.LastfmUtility(null, proxy);
        }

        private void InitGui()
        {
            listViewFiles.Columns[ColName].Width = listViewFiles.Width - 5 - listViewFiles.Columns[ColCheckbox].Width - -listViewFiles.Columns[ColPosition].Width;
            listViewTrackInfos.Columns[ColName].Width = listViewTrackInfos.Width - 5 - listViewTrackInfos.Columns[ColCheckbox].Width - listViewTrackInfos.Columns[ColPosition].Width;

            /*
            IList<KeyValuePair<ARTIST_ITEMS_SEARCH, string>> uListArtistItemsSearch = new List<KeyValuePair<ARTIST_ITEMS_SEARCH, string>>();

            uListArtistItemsSearch.Add(new KeyValuePair<ARTIST_ITEMS_SEARCH, string>(ARTIST_ITEMS_SEARCH.TOP_ALBUMS | ARTIST_ITEMS_SEARCH.TOP_TRACKS, "Albums/Tracks"));
            uListArtistItemsSearch.Add(new KeyValuePair<ARTIST_ITEMS_SEARCH, string>(ARTIST_ITEMS_SEARCH.TOP_ALBUMS, "Albums"));
            uListArtistItemsSearch.Add(new KeyValuePair<ARTIST_ITEMS_SEARCH, string>(ARTIST_ITEMS_SEARCH.TOP_TRACKS, "Tracks"));

            ComboBox cmb = this.toolStripComboBoxArtistItemsSearch.ComboBox;

            cmb.DataSource = uListArtistItemsSearch;
            cmb.DisplayMember = "Value";
            cmb.ValueMember = "Key";

            cmb.SelectedIndex = 0;
            */

            /*
            IList<KeyValuePair<DB_SITE, string>> uListDbSites = new List<KeyValuePair<DB_SITE, string>>();

            uListDbSites.Add(new KeyValuePair<DB_SITE, string>(DB_SITE.LASTFM, "Lastfm"));
            uListDbSites.Add(new KeyValuePair<DB_SITE, string>(DB_SITE.MUSICBRAINZ, "MusicBrainz"));

            cmb = this.toolStripComboBoxDbSite.ComboBox;

            cmb.DataSource = uListDbSites;
            cmb.DisplayMember = "Value";
            cmb.ValueMember = "Key";

            cmb.SelectedIndex = 0;
             * */

            this.listViewTrackInfos.ItemSelectionChanged += ItemSelectionChanged;
            this.listViewFiles.ItemSelectionChanged += ItemSelectionChanged;

            ToggleSearchArtistElementsEnabled();

            IConfig config = UMTUtility.GetConfigUI().Configs["UI"];

            if (config != null)
            {
                string dbsite = config.Get("dbsite");
                
                DB_SITE defaultDbSite;

                Enum.TryParse<DB_SITE>(dbsite, out defaultDbSite);

                //this.toolStripComboBoxDbSite.ComboBox.SelectedValue = defaultDbSite;

                radioButtonMusicBrainz.Checked = defaultDbSite == DB_SITE.MUSICBRAINZ;

                //string artistSearch = config.Get("artistSearch");

                //ARTIST_ITEMS_SEARCH defaultArtistSearch;

                //Enum.TryParse<ARTIST_ITEMS_SEARCH>(artistSearch, out defaultArtistSearch);

                //this.toolStripComboBoxArtistItemsSearch.ComboBox.SelectedValue = defaultArtistSearch;

                string workpath = config.Get("workpath");

                if (Directory.Exists(workpath) == false)
                    workpath = String.Empty;

                if (String.IsNullOrEmpty(workpath))
                    ShowMessage("Click the open path button to select working path or drag files to tag", MESSAGE_TYPE.INFO);
                else
                {
                    string filter = UMTUtility.NormalizeFilterPath(textBoxFilter.Text);

                    RefreshFilelist(workpath, filter);
                }

                this.textBoxPath.Text = workpath;
            }
        }

        private void RefreshFilelist(string path, string filter)
        {   
            string[] files = null;
            
            try
            {
                files = Directory.GetFiles(path, filter, SearchOption.TopDirectoryOnly);

                listViewFiles.Items.Clear();

                int idx = 0;

                foreach (string file in files)
                {
                    ListViewItem lvi = new ListViewItem(new[]
                    {
                        String.Empty,
                        (++idx).ToString("00"),
                        Path.GetFileName(file)
                    });

                    listViewFiles.Items.Add(lvi);
                }

                ListViewFilesItemSelected();

                ToggleButtonReorderTrackInfosEnabled();

                int filesCount = listViewFiles.Items.Count;

                int tracksCount = listViewTrackInfos.Items.Count;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void buttonApplyFilter_Click(object sender, EventArgs e)
        {
            if (Directory.Exists(textBoxPath.Text))
            {
                string path = textBoxPath.Text;

                string filter = UMTUtility.NormalizeFilterPath(textBoxFilter.Text);

                RefreshFilelist(path, filter);                
            }
        }

        private void buttonOpenPath_Click_1(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();

            string path = textBoxPath.Text;

            dialog.SelectedPath = path;

            DialogResult result = dialog.ShowDialog();

            if (result == System.Windows.Forms.DialogResult.OK)
            {
                path = dialog.SelectedPath;

                string filter = UMTUtility.NormalizeFilterPath(textBoxFilter.Text);

                textBoxPath.Text = path;

                RefreshFilelist(path, filter);

                int filesCount = listViewFiles.Items.Count;

                int tracksCount = listViewTrackInfos.Items.Count;

                if (filesCount == 0)
                {
                    ShowMessage("No items found, fix the extension filter or select a path with valid audio files", MESSAGE_TYPE.WARNING);
                }
                else if (tracksCount > 0)
                {
                    ShowMessage("Select tracks to tag on the left panel and optionally the tag mode", MESSAGE_TYPE.INFO);
                }
                else
                {
                    ShowMessage("Select files to tag manually or search artist's work on tag panel", MESSAGE_TYPE.INFO);
                }
            }
        }

        private void buttonLastfm_Click(object sender, EventArgs e)
        {
            SearchAlbum();
        }

        private void SearchTitle()
        {
            TrackInfo track = (TrackInfo)comboBoxTitleLastfm.SelectedValue;
            string title = Regex.Replace(comboBoxTitleLastfm.Text, @" \[mbid:[\w-]*]$", String.Empty);            
            //string title = Regex.Replace(comboBoxTitleLastfm.Text, @" \[mbid]$", String.Empty);            
            string artist = textBoxArtistLastfm.Text;
            string mbid = track != null ? track.Mbid : null;

            try
            {

                bool isValidCall = String.IsNullOrEmpty(mbid) == false || ((String.IsNullOrEmpty(title) || String.IsNullOrEmpty(artist)) == false);

                if (isValidCall == false)
                    throw new ApplicationException("title and artist are required fields");

                //DB_SITE dbSite = (DB_SITE)this.toolStripComboBoxDbSite.ComboBox.SelectedValue;

                DB_SITE dbSite = GetDbSiteSelected();

                List<TrackInfo> output = null;

                switch (dbSite)
                {
                    case DB_SITE.LASTFM:
                        output = 
                            new List<TrackInfo>( new TrackInfo[] { 
                                String.IsNullOrEmpty(mbid) == false ? 
                                lastfmUtility.GetTrack(mbid) :
                                lastfmUtility.GetTrack(title, artist) 
                            }) ;
                        break;
                    case DB_SITE.MUSICBRAINZ:
                        output = String.IsNullOrEmpty(mbid) == false ? 
                            new List<TrackInfo>( new TrackInfo[]{ musicBrainzUtility.GetRecording(mbid)} ):
                            musicBrainzUtility.GetRecordingList(title, artist);
                        break;
                }

                if (output.Count == 1)
                {
                    if (output[0].Releases.Length > 1)
                    {
                        toolTip1.SetToolTip(comboBoxAlbumLastfm, "Select an album from the list");                        
                        FillComboAlbum(output[0].Releases.ToList());

                        ShowMessage("Search completed, select an album from the list", MESSAGE_TYPE.INFO);
                    }
                    else
                    {
                        FillListViewTracks(output);

                        ShowMessage("Search completed", MESSAGE_TYPE.INFO);
                    }
                }
                else
                {
                    toolTip1.SetToolTip(comboBoxTitleLastfm, "Select a track from the list");                    
                    FillComboTrack(output);

                    ShowMessage("Search completed, select a track from the list", MESSAGE_TYPE.INFO);
                }
            }
            catch (ApplicationException e)
            {
                MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void SearchAlbum()
        {
            ReleaseInfo release = (ReleaseInfo)comboBoxAlbumLastfm.SelectedValue;
            string album = Regex.Replace(comboBoxAlbumLastfm.Text, @" \[mbid:[\w-]*]$", String.Empty);
            //string album = Regex.Replace(comboBoxAlbumLastfm.Text, @" \[mbid]$", String.Empty);
            string artist = textBoxArtistLastfm.Text;
            string mbid = release != null ? release.Mbid : null;

            try
            {

                bool isValidCall = String.IsNullOrEmpty(mbid) == false || ((String.IsNullOrEmpty(artist) || String.IsNullOrEmpty(album)) == false);

                if (isValidCall == false)
                    throw new ApplicationException("album and artist are required fields");

                //DB_SITE dbSite = (DB_SITE)this.toolStripComboBoxDbSite.ComboBox.SelectedValue;

                DB_SITE dbSite = GetDbSiteSelected();

                List<ReleaseInfo> output = null;

                switch (dbSite)
                {
                    case DB_SITE.LASTFM:
                        output = new List<ReleaseInfo>( new ReleaseInfo[] { String.IsNullOrEmpty(mbid) == false ? 
                            lastfmUtility.GetRelease(mbid) :
                            lastfmUtility.GetRelease(album, artist) } );
                        break;
                    case DB_SITE.MUSICBRAINZ:
                        output = String.IsNullOrEmpty(mbid) == false ? 
                            new List<ReleaseInfo>( new ReleaseInfo[]{ musicBrainzUtility.GetRelease(mbid) } ) :                        
                            musicBrainzUtility.GetReleaseList(album, artist);
                        break;
                }

                if (output.Count == 1)
                {
                    FillListViewTracks(output[0]);
                }
                else
                {
                    FillComboAlbum(output);

                    ShowMessage("Search completed, select an album from the list", MESSAGE_TYPE.INFO);
                }
            }
            catch (ApplicationException e)
            {
                MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private void FillListViewTracks(ReleaseInfo release)
        {
            listViewTrackInfos.Items.Clear();

            foreach (TrackInfo track in release.TrackInfos)
            {
                track.Releases = new ReleaseInfo[] { release };

                ListViewItem lvi = new ListViewItem(new[]
                    {
                        String.Empty,
                        track.Track.ToString("00"),
                        track.Title
                    });

                lvi.Tag = track;

                listViewTrackInfos.Items.Add(lvi);
            }

            ToggleCheckAllOnListview(listViewTrackInfos);

            //listViewTrackInfos.Tag = output;

            ToggleButtonReorderTrackInfosEnabled();
            
            int filesCount = listViewFiles.Items.Count;

            if (filesCount > 0)
            {
                ToggleButtonTagListEnabled();

                ShowMessage("Search completed", MESSAGE_TYPE.INFO);
            }
            else
            {
                ShowMessage("Click the open path button to select working path or drag files to tag", MESSAGE_TYPE.INFO);
            }
        }

        

        private void FillListViewTracks(List<TrackInfo> tracks)
        {
            listViewTrackInfos.Items.Clear();

            foreach (TrackInfo track in tracks)
            {
                ListViewItem lvi = new ListViewItem(new[]
                    {
                        String.Empty,
                        track.Track.ToString("00"),
                        track.Title
                    });

                lvi.Tag = track;

                listViewTrackInfos.Items.Add(lvi);
            }

            //listViewTrackInfos.Tag = output;

            ToggleButtonReorderTrackInfosEnabled();
        }


        [Obsolete()]
        private void FillListViewTracks(OutputInfo output)
        {
            TrackInfo[] tracks = output.TrackInfos;   

            listViewTrackInfos.Items.Clear();

            foreach (TrackInfo track in tracks)
            {
                ListViewItem lvi = new ListViewItem(new[]
                    {
                        String.Empty,
                        track.Track.ToString("00"),
                        track.Title
                    });

                lvi.Tag = track;

                listViewTrackInfos.Items.Add(lvi);
            }

            listViewTrackInfos.Tag = output;

            ToggleButtonReorderTrackInfosEnabled();
        }

        private void FillForm(ReleaseInfo output)
        {
            this.textBoxAlbum.Text = output.Title;
            this.textBoxArtist.Text = output.Artists != null ? String.Join(", ", output.Artists.Select(o => o.Name)) : null;
            //this.textBoxGenres.Text = String.Join(", ", output.ge ?? output.Artists.Select(o => o.Name));
            this.textBoxYear.Text = output.Year != null ? output.Year.ToString() : null;
            this.pictureBoxImage.ImageLocation = output.ImagePath;
            this.pictureBoxImage.Image = output.Picture;
        }

        private void FillForm(TrackInfo trackinfo)
        {
            this.textBoxTitle.Text = trackinfo.Title;
            this.textBoxPosition.Text = trackinfo.Track.ToString();
            this.textBoxTrackArtists.Text = trackinfo.Artists != null ? 
                String.Join(", ", trackinfo.Artists.Select(o => o.Name)) : null;            
        }

        private void ListViewFilesItemSelected()
        {
            if (checkBoxLockPanel.Checked == false)
            {
                int selectedItemsCount = listViewFiles.SelectedIndices.Count;

                if (selectedItemsCount > 0)
                {
                    string folder = textBoxPath.Text;
                    string file = listViewFiles.SelectedItems[0].SubItems[ColName].Text;

                    string filename = Path.Combine(folder, file);

                    try
                    {
                        TrackInfo output = umtTagger.GetTag(filename);

                        FillForm(output);
                        FillForm(output.Releases[0]);

                    }
                    catch (Exception)
                    {

                    }

                    this.labelMessage.Text = String.Format("{0} <F>", file);
                }
            }

            RefreshFilenameByTagSample();

            //ToggleButtonTagListEnabled();

        }

        private void RefreshFilenameByTagSample()
        {
            string newname = null;

            int filesSelectedCount = listViewFiles.SelectedItems.Count;

            if (filesSelectedCount == 1)
            {
                string folder = textBoxPath.Text;
                string file = listViewFiles.SelectedItems[0].SubItems[ColName].Text;

                string filename = Path.Combine(folder, file);

                string pattern = comboBoxExampleMask.Text;

                if (String.IsNullOrEmpty(pattern) == false)
                {
                    try
                    {
                        newname = umtTagger.ShowNameByTag(filename, pattern);
                    }
                    catch (Exception)
                    {
                        newname = "error";
                    }
                }
                else
                    newname = "Enter a pattern example";
            }
            else if (filesSelectedCount > 1)
                newname = "N/A";
            else                
                newname = "No file selected";

            this.labelNewFilename.Text = newname;
        }


        private void buttonLastfmSearchTitle_Click(object sender, EventArgs e)
        {
            SearchTitle();
        }

        private void listViewTrackInfos_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListViewTrackInfoItemSelected();
        }

        private void ListViewTrackInfoItemSelected()
        {
            int selectedItems = listViewTrackInfos.SelectedItems.Count;
            int selectedFiles = listViewFiles.SelectedItems.Count;

            if (selectedItems > 0)
            {
                TrackInfo track = (TrackInfo)listViewTrackInfos.SelectedItems[0].Tag;

                ReleaseInfo release = track.Releases.Length > 0 ? track.Releases[0] : new ReleaseInfo();

                FillForm(release);
                FillForm(track);

                this.labelMessage.Text = String.Format("{0} <T>", ((TrackInfo)listViewTrackInfos.SelectedItems[0].Tag).Title);
            }
        }

        private void ToggleButtonTagListEnabled()
        {
            TAG_LIST_MODE tagmode = GetTagModeSelected();

            bool enable = false;

            switch (tagmode)
            {
                case TAG_LIST_MODE.SELECTION:
                    enable = listViewTrackInfos.CheckedItems.Count > 0;
                    break;
                case TAG_LIST_MODE.NAME:
                case TAG_LIST_MODE.POSITION:
                    enable = listViewFiles.CheckedItems.Count > 0 && listViewTrackInfos.Items.Count > 0;
                    break;
            }

            this.buttonTagList.Enabled = enable;
        }

        private void CheckListViewItems(ListView leading)
        {
            ListView dependent = leading == this.listViewTrackInfos ? this.listViewFiles : this.listViewTrackInfos;

            ListView.CheckedListViewItemCollection lvicLeadingItems = leading.CheckedItems;
            ListView.ListViewItemCollection lviDependentItems = dependent.Items;

            lviDependentItems.Cast<ListViewItem>().Select((o) => o.Checked = false).ToList();

            int listViewDependentCount = lviDependentItems.Count;

            foreach (ListViewItem lvi in lvicLeadingItems)
            {
                if (lvi.Index < listViewDependentCount)
                {
                    lviDependentItems[lvi.Index].Checked = true;
                }
            }
        }

        [Obsolete("Use CheckListViewFileItems", true)]
        private void MarkListViewFiles()
        {
            ListView.SelectedListViewItemCollection lvisTracks = listViewTrackInfos.SelectedItems;
            ListView.ListViewItemCollection lvisFiles = this.listViewFiles.Items;

            int listViewFilesCount = this.listViewFiles.Items.Count;

            foreach (ListViewItem lvi in lvisFiles)
            {
                lvi.BackColor = Color.Transparent;                
            }

            foreach (ListViewItem lvi in lvisTracks)
            {
                if (lvi.Index < listViewFilesCount)
                {
                    this.listViewFiles.Items[lvi.Index].BackColor = Color.SandyBrown;
                }
            }

        }

        private void buttonTrackMoveUp_Click(object sender, EventArgs e)
        {
            MoveListViewItems(listViewTrackInfos, LISTVIEWITEMS_DIRECTIONS.UP);
        }

        private void buttonFileMoveUp_Click(object sender, EventArgs e)
        {
            MoveListViewItems(listViewFiles, LISTVIEWITEMS_DIRECTIONS.UP);
            UpdateListViewItemPosition(listViewFiles, ColPosition);
        }

        private void UpdateListViewItemPosition(ListView lv, int subitem)
        {
            int idx = 0;

            foreach (ListViewItem lvi in lv.Items)
            {
                lvi.SubItems[subitem].Text = (++idx).ToString("00");
            }
        }

        private void buttonTrackMoveDown_Click(object sender, EventArgs e)
        {
            MoveListViewItems(listViewTrackInfos, LISTVIEWITEMS_DIRECTIONS.DOWN);
        }

        private void buttonFileMoveDown_Click(object sender, EventArgs e)
        {
            MoveListViewItems(listViewFiles, LISTVIEWITEMS_DIRECTIONS.DOWN);
            UpdateListViewItemPosition(listViewFiles, ColPosition);
        }

        enum LISTVIEWITEMS_DIRECTIONS { UP, DOWN };

        private void SwitchListItems(List<TrackInfo> list, int idx1, int idx2)
        {
            TrackInfo tmp1 = list[idx1];
            TrackInfo tmp2 = list[idx2];

            int max = Math.Max(idx1, idx2);
            int min = Math.Min(idx1, idx2);

            if (max == idx2)
            {
                list.Insert(idx2, tmp1);
                list.Insert(idx1, tmp2);
            }
            else
            {
                list.Insert(idx1, tmp2);
                list.Insert(idx2, tmp1);                
            }

            list.RemoveAt(max + 2);
            list.RemoveAt(min + 1);            
        }

        private void MoveListViewItems(ListView lv, int idx1, int idx2)
        {
            if (idx1 == idx2) return;

            ListViewItem cacheItemUp = null;
            ListViewItem cacheItemDown = null;

            System.Windows.Forms.ListView.ListViewItemCollection lvic = lv.Items;

            cacheItemUp = (ListViewItem)lvic[idx1].Clone();
            cacheItemDown = (ListViewItem)lvic[idx2].Clone();

            int max = Math.Max(idx1, idx2);
            int min = Math.Min(idx1, idx2);

            lvic.RemoveAt(max);
            lvic.RemoveAt(min);

            if (idx1 == min)
            {
                lvic.Insert(idx1, cacheItemDown);
                lvic.Insert(idx2, cacheItemUp);
            }
            else
            {
                lvic.Insert(idx2, cacheItemUp);
                lvic.Insert(idx1, cacheItemDown);                
            }

        }

        private void MoveListViewItems(ListView lv, LISTVIEWITEMS_DIRECTIONS direction)
        {
            ListViewItem cacheItemUp = null;
            ListViewItem cacheItemDown = null;

            System.Windows.Forms.ListView.ListViewItemCollection lvic = lv.Items;

            if (lvic.Count > 0)
            {
                if (direction == LISTVIEWITEMS_DIRECTIONS.UP)
                {
                    foreach (int selIdx in lv.SelectedIndices)
                    {
                        // ignore moveup of row(0)
                        if (selIdx == 0)
                            break;

                        cacheItemUp = (ListViewItem)lvic[selIdx - 1].Clone();
                        cacheItemDown = (ListViewItem)lvic[selIdx].Clone();

                        lvic.RemoveAt(selIdx - 1);
                        lvic.RemoveAt(selIdx - 1);

                        lvic.Insert(selIdx - 1, cacheItemDown);
                        lvic.Insert(selIdx, cacheItemUp);
                        
                        lvic[selIdx].Selected = false;
                        lvic[selIdx - 1].Selected = true;
                    }
                }
                else
                {
                    // move the subitems for the next row
                    // to cache so we can move the selected row down
                    for (int idx = lv.SelectedIndices.Count; --idx >= 0; )
                    {
                        int selIdx = lv.SelectedIndices[idx];

                        // ignore movedown of last item
                        if (selIdx == lvic.Count - 1)
                            continue;

                        //for (int i = 0; i < lvic[selIdx].SubItems.Count; i++)
                        //{
                            //cache = lvic[selIdx + 1].SubItems[i].Text;
                            //lvic[selIdx + 1].SubItems[i].Text = lvic[selIdx].SubItems[i].Text;
                            //lvic[selIdx].SubItems[i].Text = cache;

                        cacheItemUp = (ListViewItem)lvic[selIdx].Clone();
                        cacheItemDown = (ListViewItem)lvic[selIdx + 1].Clone();

                        lvic.RemoveAt(selIdx);
                        lvic.RemoveAt(selIdx);

                        lvic.Insert(selIdx, cacheItemDown);
                        lvic.Insert(selIdx + 1, cacheItemUp);

                        //}
                        lvic[selIdx].Selected = false;
                        lvic[selIdx + 1].Selected = true;
                    }
                }
            }

            lv.Refresh();
            lv.Focus();
        }

        private void buttonTag_Click(object sender, EventArgs e)
        {
            TAG_FIELDS tagFields = GetTagFieldsFromForm();

            string folder = textBoxPath.Text;

            uint year = GetYearFromDate(textBoxYear.Text);
            uint pos = 0;

            //UInt32.TryParse(textBoxYear.Text, out year);
            UInt32.TryParse(textBoxPosition.Text, out pos);

            ModelTag input = new ModelTag
            {
                Album = textBoxAlbum.Text,
                AlbumArtists = textBoxArtist.Text.Split(','),
                TrackArtists = textBoxTrackArtists.Text.Split(','),
                Genres = textBoxGenres.Text.Split(','),
                Picture = pictureBoxImage.Image,
                Position = pos,
                Year = year,
                Title = textBoxTitle.Text                
            };

            int success = 0;
            int failure = 0;

            int checkedFilesCount = listViewFiles.CheckedItems.Count;

            bool showPrompt = (checkedFilesCount > 1 && (tagFields & (TAG_FIELDS.TRACK_POS | TAG_FIELDS.TITLE)) != TAG_FIELDS.NONE);
            bool okToContinue = true;

            if (showPrompt)
            {
                DialogResult result = MessageBox.Show("Are you sure to tag multiple files with same individual track tag?", String.Empty, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                okToContinue = result == System.Windows.Forms.DialogResult.Yes;
            }

            if (okToContinue)
            {
                foreach (ListViewItem lvi in listViewFiles.CheckedItems)
                {
                    string file = lvi.SubItems[ColName].Text;

                    string filename = Path.Combine(folder, file);

                    try
                    {
                        umtTagger.TagFile(filename, input, tagFields);

                        success++;
                    }
                    catch (Exception ex)
                    {
                        failure++;
                    }
                }

                if (success == 0)
                {
                    ShowMessage(String.Format("No files were tagged, {0} failed", failure), MESSAGE_TYPE.ERROR);
                }
                else if (success > 0 && failure > 0)
                {
                    ShowMessage(String.Format("Tag operation completed. {0} success, {1} failed", success, failure), MESSAGE_TYPE.WARNING);
                }
                else
                {
                    ShowMessage(String.Format("Tag operation completed. {0} success with no errors", success), MESSAGE_TYPE.INFO);
                }
            }
        }

        private void buttonImportImage_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            string sep = string.Empty;
            string filter = String.Empty;
            string filterAllImages = String.Empty;

            foreach (var c in codecs)
            {
                string codecName = c.CodecName.Substring(8).Replace("Codec", "Files").Trim();
                filter = String.Format("{0}{1}{2} ({3})|{3}", filter, sep, codecName, c.FilenameExtension);
                filterAllImages = String.Format("{0}{1};", filterAllImages, c.FilenameExtension);
                sep = "|";
            }

            openFileDialog.Filter = String.Format("{4} |{5}{1}{0}{1}{2} ({3})|{3}", filter, sep, "All Files", "*.*", "All Images", filterAllImages);

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                string path = openFileDialog.FileName;

                this.pictureBoxImage.Image = Image.FromFile(path);
            }
        }

        private void buttonClearImage_Click(object sender, EventArgs e)
        {
            this.pictureBoxImage.Image = null;
        }

        private void buttonExportImage_Click(object sender, EventArgs e)
        {
            if (this.pictureBoxImage.Image != null)
            {
                // Displays a SaveFileDialog so the user can save the Image
                // assigned to Button2.
                SaveFileDialog saveFileDialog1 = new SaveFileDialog();
                saveFileDialog1.Filter = "JPeg Image|*.jpg|Bitmap Image|*.bmp|Gif Image|*.gif";
                saveFileDialog1.Title = "Save an Image File";
                DialogResult result = saveFileDialog1.ShowDialog();

                // If the file name is not an empty string open it for saving.
                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    ImageFormat format = null;                    

                    // Saves the Image via a FileStream created by the OpenFile method.
                    System.IO.FileStream fs =
                       (System.IO.FileStream)saveFileDialog1.OpenFile();
                    // Saves the Image in the appropriate ImageFormat based upon the
                    // File type selected in the dialog box.
                    // NOTE that the FilterIndex property is one-based.
                    switch (saveFileDialog1.FilterIndex)
                    {
                        case 1:
                            format = ImageFormat.Jpeg;
                            break;
                        case 2:
                            format = ImageFormat.Bmp;
                            break;

                        case 3:
                            format = ImageFormat.Gif;
                            break;
                    }

                    this.pictureBoxImage.Image.Save(fs, format);

                    fs.Close();
                }
            }
            else
                MessageBox.Show("No image is currently loaded in the cover box", null, MessageBoxButtons.OK, MessageBoxIcon.Error);


        }

        private void listViewTrackInfos_Click(object sender, EventArgs e)
        {
            ListViewTrackInfoItemSelected();
        }

        private uint GetYearFromDate(string date)
        {
            string pattern = "\\d{4}";

            Match match = Regex.Match(date ?? String.Empty, pattern);

            uint ret = 0;

            uint.TryParse(match.Value, out ret);

            return ret;
        }

        private void TaglistBySelection()
        {
            TAG_FIELDS tagFields = GetTagFieldsFromForm();

            ListView.CheckedListViewItemCollection lvicTracks = listViewTrackInfos.CheckedItems;

            int success = 0;
            int failure = 0;

            Image picture = null;

            foreach (ListViewItem lvi in lvicTracks)
            {
                if (lvi.Index < this.listViewFiles.Items.Count)
                {
                    TrackInfo trackInfo = (TrackInfo)lvi.Tag;

                    ReleaseInfo releaseInfo = trackInfo.Releases[0];

                    string folder = textBoxPath.Text;
                    string file = listViewFiles.Items[lvi.Index].SubItems[ColName].Text;

                    string filename = Path.Combine(folder, file);

                    if (picture == null && String.IsNullOrEmpty(releaseInfo.ImagePath) == false) 
                        picture = MTUtility.ImageFromUri(releaseInfo.ImagePath, proxy);

                    try
                    {
                        ModelTag input = new ModelTag
                        {
                            Title = trackInfo.Title,
                            Album = releaseInfo.Title,
                            AlbumArtists = releaseInfo.Artists != null ? releaseInfo.Artists.Select(a => a.Name).ToArray() : null,
                            Genres = null,
                            ArtistMbid = trackInfo.Artists != null ? trackInfo.Artists[0].Mbid : null,
                            Picture = picture,
                            ReleaseMbid = releaseInfo.Mbid,
                            TrackMbid = trackInfo.Mbid,
                            Position = trackInfo.Track,
                            Year = GetYearFromDate( releaseInfo.Year ),
                            TrackArtists = trackInfo.Artists != null ? trackInfo.Artists.Select(a => a.Name).ToArray() : null,
                        };

                        umtTagger.TagFile(filename, input, tagFields);

                        success++;
                    }
                    catch (Exception)
                    {
                        failure++;
                    }
                }
            }

            //this.labelMessage.Text = String.Format("Tag operation completed. {0} success, {1} failed", success, failure);

            if (success == 0)
            {
                ShowMessage(String.Format("No files were tagged, {0} failed", failure), MESSAGE_TYPE.ERROR);
            }
            else if (success > 0 && failure > 0)
            {
                ShowMessage(String.Format("Tag operation completed. {0} success, {1} failed", success, failure), MESSAGE_TYPE.WARNING);
            }
            else
            {
                ShowMessage(String.Format("Tag operation completed. {0} success with no errors", success), MESSAGE_TYPE.INFO);
            }
        }


        //[Obsolete()]
        //private void TaglistBySelection()
        //{
        //    TAG_FIELDS tagFields = GetTagFieldsFromForm();

        //    ListView.SelectedListViewItemCollection lvis = listViewTrackInfos.SelectedItems;

        //    OutputInfo output = (OutputInfo)listViewTrackInfos.Tag;

        //    ModelTag input = new ModelTag
        //    {
        //        Album = output.Album,
        //        AlbumArtists = output.ArtistsTrack,
        //        Genres = output.Genres,
        //        ArtistMbid = output.ArtistMbid,
        //        Picture = output.ImageData,
        //        ReleaseMbid = output.ReleaseMbid,
        //        Year = output.Year
        //    };

        //    int success = 0;
        //    int failure = 0;

        //    foreach (ListViewItem lvi in lvis)
        //    {
        //        if (lvi.Index < this.listViewFiles.Items.Count)
        //        {
        //            TrackInfo trackInfo = (TrackInfo)lvi.Tag;

        //            input.Title = trackInfo.Title;
        //            input.Position = trackInfo.Track;
        //            input.TrackMbid = trackInfo.Mbid;

        //            string folder = textBoxPath.Text;
        //            string file = listViewFiles.Items[lvi.Index].SubItems[1].Text;

        //            string filename = Path.Combine(folder, file);

        //            try
        //            {
        //                umtTagger.TagFile(filename, input, tagFields);

        //                success++;
        //            }
        //            catch (Exception)
        //            {
        //                failure++;
        //            }
        //        }
        //    }

        //    this.labelMessage.Text = String.Format("Tag operation completed. {0} success, {1} failed", success, failure);
        //}

        private void TaglistByComparer(TAG_LIST_MODE mode)
        {
            TAG_FIELDS tagFields = GetTagFieldsFromForm();

            ListView.CheckedListViewItemCollection lvicFiles = listViewFiles.CheckedItems;

            ListView.ListViewItemCollection lvisTracks = listViewTrackInfos.Items;

            TrackInfo track = (TrackInfo)lvisTracks[0].Tag;

            ReleaseInfo releaseInfo = track.Releases[0];

            List<TrackInfo> list = (from lvi in lvisTracks.Cast<ListViewItem>()
                                    select (TrackInfo)lvi.Tag).ToList();

            Func<string, List<TrackInfo>, TrackInfo> comparer = null;

            switch (mode)
            {
                case TAG_LIST_MODE.NAME:
                    comparer = MTUtility.GetTrackInfoByLevenshteinDistance;
                    break;
                case TAG_LIST_MODE.POSITION:
                    comparer = MTUtility.GetTrackInfoByPosition;
                    break;
            }

            int success = 0;
            int failure = 0;
            int skipped = 0;

            foreach (ListViewItem lvi in lvicFiles)
            {                
                string folder = textBoxPath.Text;
                string file = listViewFiles.Items[lvi.Index].SubItems[ColName].Text;

                string filename = Path.Combine(folder, file);

                string filenameWithoutExtension = Path.GetFileNameWithoutExtension(file);

                TrackInfo trackInfo = comparer(filenameWithoutExtension, list);

                if (trackInfo != null)
                {
                    ModelTag input = new ModelTag
                    {
                        Title = trackInfo.Title,
                        Album = releaseInfo.Title,
                        AlbumArtists = releaseInfo.Artists != null ? releaseInfo.Artists.Select(a => a.Name).ToArray() : null,
                        Genres = null,
                        ArtistMbid = trackInfo.Artists != null ? trackInfo.Artists[0].Mbid : null,
                        Picture = MTUtility.ImageFromUri(releaseInfo.ImagePath, proxy),
                        ReleaseMbid = releaseInfo.Mbid,
                        TrackMbid = trackInfo.Mbid,
                        Position = trackInfo.Track,
                        Year = GetYearFromDate( releaseInfo.Year ),
                        TrackArtists = trackInfo.Artists != null ? trackInfo.Artists.Select(a => a.Name).ToArray() : null,
                    };

                    try
                    {
                        umtTagger.TagFile(filename, input, tagFields);

                        success++;
                    }
                    catch (Exception)
                    {
                        failure++;
                    }
                }
                else
                    skipped++;
            }

            if (success == 0)
            {
                ShowMessage(String.Format("No files were tagged, {0} failed, {1} skipped", failure, skipped), MESSAGE_TYPE.ERROR);
            }
            else if (success > 0 && failure > 0)
            {
                ShowMessage(String.Format("Tag operation completed. {0} success, {1} failed, {2} skipped", success, failure, skipped), MESSAGE_TYPE.WARNING);
            }
            else
            {
                ShowMessage(String.Format("Tag operation completed. {0} success with no errors, {1} skipped", success, skipped), MESSAGE_TYPE.INFO);
            }
        }

        private TAG_LIST_MODE GetTagModeSelected()
        {
            TAG_LIST_MODE tagMode;

            if (radioButtonTaglistSelection.Checked)
                tagMode = TAG_LIST_MODE.SELECTION;
            else if (radioButtonTaglistName.Checked)
                tagMode = TAG_LIST_MODE.NAME;
            else
                tagMode = TAG_LIST_MODE.POSITION;

            return tagMode;
        }

        private void buttonTagList_Click(object sender, EventArgs e)
        {
            TAG_LIST_MODE tagMode = GetTagModeSelected();

            switch (tagMode)
            {
                case TAG_LIST_MODE.SELECTION:
                    TaglistBySelection();
                    break;
                case TAG_LIST_MODE.NAME:                    
                case TAG_LIST_MODE.POSITION:
                    TaglistByComparer(tagMode);
                    break;
            }

            //ListViewFilesItemSelected();
        }

        private TAG_FIELDS GetTagFieldsFromForm()
        {
            TAG_FIELDS tagFields = TAG_FIELDS.NONE;

            if (checkBoxAlbum.Checked) tagFields |= TAG_FIELDS.ALBUM;
            if (checkBoxAlbumArtist.Checked) tagFields |= TAG_FIELDS.ALBUM_ARTIST;
            if (checkBoxTrackArtists.Checked) tagFields |= TAG_FIELDS.TRACK_ARTIST;
            if (checkBoxTitle.Checked) tagFields |= TAG_FIELDS.TITLE;
            if (checkBoxPosition.Checked) tagFields |= TAG_FIELDS.TRACK_POS;
            if (checkBoxYear.Checked) tagFields |= TAG_FIELDS.YEAR;
            if (checkBoxGenres.Checked) tagFields |= TAG_FIELDS.GENRES;
            if (checkBoxImage.Checked) tagFields |= TAG_FIELDS.IMAGE;

            return tagFields;
        }

        private void checkBoxRenameFiles_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void checkBoxIncludeDir_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void comboBoxMask_SelectedValueChanged(object sender, EventArgs e)
        {

        }

        private void buttonPaste_Click(object sender, EventArgs e)
        {
            string examplePattern = this.comboBoxExampleMask.Text;

            this.comboBoxPatternFile.Text = examplePattern;
            this.comboBoxPatternFolder.Text = examplePattern;
        }

        private void textBoxPath_TextChanged(object sender, EventArgs e)
        {            
            ToggleButtonRenameFolderEnabled();
        }

        private void buttonRenameFolder_Click(object sender, EventArgs e)
        {
            RenameFolder();
        }

        private void buttonRenameFiles_Click(object sender, EventArgs e)
        {
            RenameCheckedFiles();
        }

        private void RenameFolder()
        {
            string path = textBoxPath.Text;

            string[] files = null;

            string pattern = comboBoxPatternFolder.Text;

            files = (from file in listViewFiles.CheckedItems.Cast<ListViewItem>()
                     select (Path.Combine(path, ((ListViewItem.ListViewSubItem)file.SubItems[ColName]).Text))).ToArray();

            bool taskerror = false;

            if (files.Length > 0)
            {
                try
                {
                    string newfoldername = umtTagger.RenameFolderByTag(path, files, pattern);

                    textBoxPath.Text = newfoldername;

                    ShowMessage("Folder renamed", MESSAGE_TYPE.INFO);
                }
                catch (ApplicationException e)
                {
                    taskerror = true;
                    MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                catch (Exception e)
                {
                    taskerror = true;
                    MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
                ShowMessage("Check files on the right panel with same release tag to rename parent folder", MESSAGE_TYPE.WARNING);

            if (taskerror)
                ShowMessage("Folder was not renamed", MESSAGE_TYPE.ERROR);
        }


        private void RenameCheckedFiles()
        {
            string filenamePattern = comboBoxPatternFile.Text;

            string[] files = null;

            string pattern = comboBoxPatternFile.Text;
                
            string path = textBoxPath.Text;

            files = (from file in listViewFiles.CheckedItems.Cast<ListViewItem>()
                    select (Path.Combine( path , ( (ListViewItem.ListViewSubItem)file.SubItems[ColName]).Text))).ToArray();

            if (files.Length > 0)
            {
                bool result = umtTagger.RenameFilesByTag(files, pattern);

                string filter = UMTUtility.NormalizeFilterPath(textBoxFilter.Text);

                RefreshFilelist(path, filter);

                ShowMessage("Files renamed", MESSAGE_TYPE.INFO);
            }            
            else
                ShowMessage("Check files to rename on the right panel", MESSAGE_TYPE.WARNING);

        }

        private void textBoxPatternFile_TextChanged(object sender, EventArgs e)
        {
            ToggleButtonRenameFileEnabled();

        }

        private void ToggleButtonRenameFileEnabled()
        {
            this.buttonRenameFilesOld.Enabled = (listViewFiles.CheckedItems.Count > 0 && String.IsNullOrEmpty(comboBoxPatternFile.Text) == false);
        }

        private void ToggleButtonRenameFolderEnabled()
        {
            this.buttonRenameFolderOld.Enabled = (String.IsNullOrEmpty(textBoxPath.Text) == false
                && String.IsNullOrEmpty(comboBoxPatternFolder.Text) == false
                && listViewFiles.CheckedItems.Count > 0 
                );
        }

        private void textBoxPatternFolder_TextChanged(object sender, EventArgs e)
        {
            ToggleButtonRenameFolderEnabled();
        }

        private void comboBoxMask_TextChanged(object sender, EventArgs e)
        {
            RefreshFilenameByTagSample();
        }

        private void textBoxTitleLastfm_TextChanged(object sender, EventArgs e)
        {
            ToggleSearchTitleLastfmEnabled();
        }

        private void ToggleSearchTitleLastfmEnabled()
        {
            string title = this.comboBoxTitleLastfm.Text;
            string artist = this.textBoxArtistLastfm.Text;

            this.buttonLastfmSearchTitle.Enabled = title.Length > 1 && artist.Length > 1;
        }

        private void ToggleSearchAlbumLastfmEnabled()
        {
            string album = this.comboBoxAlbumLastfm.Text;
            string artist = this.textBoxArtistLastfm.Text;

            this.buttonLastfmSearchAlbum.Enabled = album.Length > 1 && artist.Length > 1;
        }


        private void textBoxArtistLastfm_TextChanged(object sender, EventArgs e)
        {
            ToggleSearchTitleLastfmEnabled();
            ToggleSearchAlbumLastfmEnabled();
            //ToggleSearchTopAlbumsLastfmEnabled();
            ToggleSearchArtistElementsEnabled();
        }

        private void ToggleSearchArtistElementsEnabled()
        {
            string artist = this.textBoxArtistLastfm.Text;

            this.buttonSearchArtistAlbums.Enabled = artist.Length > 1;
            this.buttonSearchArtistTracks.Enabled = artist.Length > 1;
        }

        private void comboBoxAlbumLastfm_TextChanged(object sender, EventArgs e)
        {
            ToggleSearchAlbumLastfmEnabled();
        }

        private void toolStripMenuItemSettings_Click(object sender, EventArgs e)
        {
            FormSettings settingsForm = new FormSettings();
            settingsForm.ShowDialog(this);

            proxy = UMTUtility.GetProxyFromConfig();
            umtTagger.SetProxy(proxy);
            musicBrainzUtility.SetProxy(proxy);
            lastfmUtility.SetProxy(proxy);
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            AboutBox aboutBox = new AboutBox();
            aboutBox.ShowDialog(this);
        }

        private void listViewTrackInfos_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            ToggleCheckAllOnListview(listViewTrackInfos);
        }

        private void OrderListViewTrackInfos(Func<string, List<TrackInfo>, TrackInfo> func)
        {
            List<TrackInfo> list = (from lvi in listViewTrackInfos.Items.Cast<ListViewItem>()
                                    select (TrackInfo)lvi.Tag).ToList();

            string[] files = (from file in listViewFiles.Items.Cast<ListViewItem>()
                              select (Path.GetFileNameWithoutExtension( ((ListViewItem.ListViewSubItem)file.SubItems[ColName]).Text))).ToArray();

            int indexFilename = 0;

            foreach (string file in files)
            {
                if (indexFilename > (list.Count - 1))
                    break;

                TrackInfo track = func(file, list);

                int indexTrackinfo = list.IndexOf(track);

                if (indexTrackinfo != -1)
                {
                    SwitchListItems(list, indexFilename, indexTrackinfo);                    
                }

                indexFilename++;
            }

            FillListViewTracks(list);   
        }

        private void buttonOrderByName_Click(object sender, EventArgs e)
        {
            OrderListViewTrackInfos(MTUtility.GetTrackInfoByLevenshteinDistance);
        }

        private void buttonOrderByPosition_Click(object sender, EventArgs e)
        {
            OrderListViewTrackInfos(MTUtility.GetTrackInfoByPosition);
        }

        private void listViewFiles_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            ToggleCheckAllOnListview(listViewFiles);          
        }

        private void ToggleCheckAllOnListview(ListView lv)
        {
            int checkedItemsCount = lv.CheckedItems.Count;

            bool checkedProperty = (checkedItemsCount == lv.Items.Count) == false;

            //lv.Items.Cast<ListViewItem>().Select(o => { o.Checked = checkedProperty; return o; }).ToList();            

            lv.Items.Cast<ListViewItem>().Select(o => o.Checked = checkedProperty).ToList();            
        }

        private void ToggleButtonReorderTrackInfosEnabled()
        {
            bool enable = this.listViewTrackInfos.Items.Count > 0 && this.listViewFiles.Items.Count > 0;

            this.buttonOrderTracksByName.Enabled = enable;
            this.buttonOrderTracksByPosition.Enabled = enable;
            this.buttonOrderFilesByName.Enabled = enable;
            this.buttonOrderFilesByPosition.Enabled = enable;
        }

        private void buttonExplorePath_Click(object sender, EventArgs e)
        {
            string path = this.textBoxPath.Text;

            if (String.IsNullOrEmpty(path) == false && Directory.Exists(path))
            {
                var runExplorer = new System.Diagnostics.ProcessStartInfo();
                runExplorer.FileName = "explorer.exe";
                runExplorer.Arguments = path;
                System.Diagnostics.Process.Start(runExplorer); 
            }            
        }

        private DB_SITE GetDbSiteSelected()
        {
            return this.radioButtonLastFM.Checked ? DB_SITE.LASTFM : DB_SITE.MUSICBRAINZ;
        }

        [Obsolete()]
        private void buttonLastfmSearchTopAlbums_Click(object sender, EventArgs e)
        {
            ARTIST_ITEMS_SEARCH search = (ARTIST_ITEMS_SEARCH)this.toolStripComboBoxArtistItemsSearch.ComboBox.SelectedValue;

            //DB_SITE dbSite = (DB_SITE)this.toolStripComboBoxDbSite.ComboBox.SelectedValue;

            DB_SITE dbSite = GetDbSiteSelected();

            if ((search & ARTIST_ITEMS_SEARCH.TOP_ALBUMS) == ARTIST_ITEMS_SEARCH.TOP_ALBUMS)
            {
                SearchAndFillAlbums(dbSite);                
            }
            if ((search & ARTIST_ITEMS_SEARCH.TOP_TRACKS) == ARTIST_ITEMS_SEARCH.TOP_TRACKS)
            {
                SearchAndFillTracks(dbSite);
            }
        }

        private void SearchAndFillTracks(DB_SITE site)
        {

            string artist = textBoxArtistLastfm.Text;

            try
            {

                bool isValidCall = String.IsNullOrEmpty(artist) == false;

                if (isValidCall == false)
                    throw new ApplicationException("artist is a required fields");

                Func<string, List<TrackInfo>> func = null;

                switch (site)
                {
                    case DB_SITE.LASTFM:
                        func = lastfmUtility.GetTrackList;
                        break;
                    case DB_SITE.MUSICBRAINZ:
                        func = musicBrainzUtility.GetRecordingList;
                        break;
                }

                List<TrackInfo> list = func(artist);

                FillComboTrack(list);

                ShowMessage("Search completed", MESSAGE_TYPE.INFO);
           
            }
            catch (ApplicationException e)
            {
                MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

        }

        private void FillComboTrack(List<TrackInfo> list)
        {
            ComboBox cmb = this.comboBoxTitleLastfm;

            IList<KeyValuePair<TrackInfo, string>> listDataSource = new List<KeyValuePair<TrackInfo, string>>();

            foreach (TrackInfo output in list)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(output.Title);
                if (String.IsNullOrEmpty( output.Mbid ) == false)
                {
                    sb.Append(" [mbid: ");
                    sb.Append(output.Mbid);
                    sb.Append("]");
                    //sb.Append(" [mbid]");
                }

                listDataSource.Add(new KeyValuePair<TrackInfo, string>(output, sb.ToString()));
            }
            
            cmb.DisplayMember = "Value";
            cmb.ValueMember = "Key";
            cmb.DataSource = listDataSource;

            cmb.Text = String.Format("[{0} elements found]", list.Count);    
        }

        private void SearchAndFillAlbums(DB_SITE site)
        {
                     
            string artist = textBoxArtistLastfm.Text;

            try
            {
                bool isValidCall = String.IsNullOrEmpty(artist) == false;

                if (isValidCall == false)
                    throw new ApplicationException("artist is a required fields");

                Func<string, List<ReleaseInfo>> func = null;

                List<ReleaseInfo> list = null;

                switch (site)
                {
                    case DB_SITE.LASTFM:
                        func = lastfmUtility.GetReleaseList;
                        break;
                    case DB_SITE.MUSICBRAINZ:
                        func = musicBrainzUtility.GetReleaseList;
                        break;
                }

                list = func(artist);

                FillComboAlbum(list);

                ShowMessage("Search completed", MESSAGE_TYPE.INFO);
            }
            catch (ApplicationException e)
            {
                MessageBox.Show(e.Message, null, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }        
        }

        private void FillComboAlbum(List<ReleaseInfo> list)
        {

            ComboBox cmb = this.comboBoxAlbumLastfm;

            IList<KeyValuePair<ReleaseInfo, string>> keyvaluelist = new List<KeyValuePair<ReleaseInfo, string>>();

            foreach (ReleaseInfo output in list)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(output.Title);
                if (String.IsNullOrEmpty( output.Mbid ) == false)
                {
                    sb.Append(" [mbid: ");
                    sb.Append(output.Mbid);
                    sb.Append("]");
                    //sb.Append(" [mbid]");
                }

                keyvaluelist.Add(new KeyValuePair<ReleaseInfo, string>(output, sb.ToString()));
            }
            
            cmb.DisplayMember = "Value";
            cmb.ValueMember = "Key";
            cmb.DataSource = keyvaluelist;

            cmb.Text = String.Format("[{0} elements found]", list.Count);

        }

        private void comboBoxTagListMode_SelectedValueChanged(object sender, EventArgs e)
        {
            TAG_LIST_MODE tagmode = GetTagModeSelected();

            switch (tagmode)
            {
                case TAG_LIST_MODE.SELECTION:
                    ShowMessage("[Selection mode] Check tracks on the left panel", MESSAGE_TYPE.INFO);
                    break;
                case TAG_LIST_MODE.NAME:
                    ShowMessage("[Name mode] Check files on the right panel [matches filename/trackname]", MESSAGE_TYPE.INFO);
                    this.listViewTrackInfos.Items.Cast<ListViewItem>().Select(o => o.Checked = false).ToList();
                    break;
                case TAG_LIST_MODE.POSITION:
                    ShowMessage("[Position mode] Check files on the right panel [matches number/trackposition]", MESSAGE_TYPE.INFO);
                    this.listViewTrackInfos.Items.Cast<ListViewItem>().Select(o => o.Checked = false).ToList();
                    break;
            }

            ToggleButtonTagListEnabled();
        }

        private void listViewTrackInfos_SizeChanged(object sender, EventArgs e)
        {
            ToggleButtonTagListEnabled();
        }

        private void ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            e.Item.BackColor = e.IsSelected ? ColorHighlight : Color.Transparent;
            e.Item.ForeColor = e.IsSelected ? ColorFontHighlight : Color.Black;
        }

        private void listViewFiles_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            bool isPanelLocked = this.checkBoxLockPanel.Checked;
            if (isPanelLocked == false)
                ListViewFilesItemSelected();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            IConfigSource configSource = UMTUtility.GetConfigUI();

            IConfig config = configSource.Configs["UI"];

            config.Set("dbsite", GetDbSiteSelected().ToString());
            config.Set("workpath", this.textBoxPath.Text);
            //config.Set("artistSearch", ((ARTIST_ITEMS_SEARCH)this.toolStripComboBoxArtistItemsSearch.ComboBox.SelectedValue).ToString());

            configSource.Save();
        }

        private void listViewFiles_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListViewFilesItemSelected();
        }

        private void comboBoxAlbumLastfm_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.comboBoxAlbumLastfm.SelectedIndex >= 0)
            {
                ReleaseInfo release = (ReleaseInfo)this.comboBoxAlbumLastfm.SelectedValue;

                this.textBoxAlbum.Text = release.Title;
                this.textBoxArtist.Text = release.Artists != null ? String.Join(", ", release.Artists.Select(o => o.Name)) : null;
                this.textBoxYear.Text = release.Year.ToString();
                this.pictureBoxImage.ImageLocation = release.ImagePath;

                this.textBoxTitle.Text = null;
                this.textBoxTrackArtists.Text = null;
                this.textBoxPosition.Text = null;
                this.textBoxGenres.Text = null;
            }
        }

        private void comboBoxTitleLastfm_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.comboBoxTitleLastfm.SelectedIndex >= 0)
            {
                TrackInfo track = (TrackInfo)this.comboBoxTitleLastfm.SelectedValue;

                ReleaseInfo release = track.Releases != null && track.Releases.Length >= 0 ? track.Releases[0] : null;

                string title = null;
                string albumArtists = null;
                string year = null;
                string image = null;

                if (release != null)
                {
                    title = release.Title;
                    albumArtists = release.Artists != null ? String.Join(", ", release.Artists.Select(o => o.Name)) : null;
                    year = release.Year.ToString();
                    image = release.ImagePath;
                }

                this.textBoxAlbum.Text = title;
                this.textBoxArtist.Text = albumArtists;
                this.textBoxYear.Text = year;
                this.pictureBoxImage.ImageLocation = image;

                this.textBoxTitle.Text = track.Title;
                this.textBoxTrackArtists.Text = track.Artists != null ? String.Join(", ", track.Artists.Select(o => o.Name)) : null;
                this.textBoxPosition.Text = track.Track.ToString();
                this.textBoxGenres.Text = null;
            }
        }

        private void buttonSearchArtistTracks_Click(object sender, EventArgs e)
        {
            DB_SITE dbSite = GetDbSiteSelected();

            SearchAndFillTracks(dbSite);            
        }

        private void buttonSearchArtistAlbums_Click(object sender, EventArgs e)
        {
            DB_SITE dbSite = GetDbSiteSelected();

            SearchAndFillAlbums(dbSite);                            
        }

        private void comboBoxAlbumLastfm_SelectedValueChanged(object sender, EventArgs e)
        {
            if (this.comboBoxAlbumLastfm.SelectedIndex >= 0)
            {
                ReleaseInfo release = (ReleaseInfo)this.comboBoxAlbumLastfm.SelectedValue;

                this.textBoxAlbum.Text = release.Title;
                this.textBoxArtist.Text = release.Artists != null ? String.Join(", ", release.Artists.Select(o => o.Name)) : null;
                this.textBoxYear.Text = release.Year != null ?  release.Year.ToString() : null;
                this.pictureBoxImage.ImageLocation = release.ImagePath;

                this.textBoxTitle.Text = null;
                this.textBoxTrackArtists.Text = null;
                this.textBoxPosition.Text = null;
                this.textBoxGenres.Text = null;
            }
        }

        private void comboBoxTitleLastfm_SelectedValueChanged(object sender, EventArgs e)
        {
            if (this.comboBoxTitleLastfm.SelectedIndex >= 0)
            {
                TrackInfo track = (TrackInfo)this.comboBoxTitleLastfm.SelectedValue;

                ReleaseInfo release = track.Releases != null && track.Releases.Length >= 0 ? track.Releases[0] : null;

                string title = null;
                string albumArtists = null;
                string year = null;
                string image = null;

                if (release != null)
                {
                    title = release.Title;
                    albumArtists = release.Artists != null ? String.Join(", ", release.Artists.Select(o => o.Name)) : null;
                    year = release.Year.ToString();
                    image = release.ImagePath;
                }

                this.textBoxAlbum.Text = title;
                this.textBoxArtist.Text = albumArtists;
                this.textBoxYear.Text = year;
                this.pictureBoxImage.ImageLocation = image;

                this.textBoxTitle.Text = track.Title;
                this.textBoxTrackArtists.Text = track.Artists != null ? String.Join(", ", track.Artists.Select(o => o.Name)) : null;
                this.textBoxPosition.Text = track.Track.ToString();
                this.textBoxGenres.Text = null;
            }
        }

        private void buttonFileMoveUp_Click_1(object sender, EventArgs e)
        {
            MoveListViewItems(listViewFiles, LISTVIEWITEMS_DIRECTIONS.UP);
            UpdateListViewItemPosition(listViewFiles, ColPosition);
        }

        private void buttonFileMoveDown_Click_1(object sender, EventArgs e)
        {
            MoveListViewItems(listViewFiles, LISTVIEWITEMS_DIRECTIONS.DOWN);
            UpdateListViewItemPosition(listViewFiles, ColPosition);

        }

        private void comboBoxPatternFile_TextChanged(object sender, EventArgs e)
        {
            //this.buttonRenameFilesOld.Enabled = this.comboBoxPatternFile.Text.Length > 1;
            this.buttonRenameFiles.Enabled = this.comboBoxPatternFile.Text.Length > 1;
        }

        private void comboBoxPatternFolder_TextChanged(object sender, EventArgs e)
        {
            //this.buttonRenameFolderOld.Enabled = this.comboBoxPatternFolder.Text.Length > 1;
            this.buttonRenameFolder.Enabled = this.comboBoxPatternFolder.Text.Length > 1;
        }

        private void buttonTrackMoveUp_Click_1(object sender, EventArgs e)
        {
            
        }

        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
            // make sure they're actually dropping files (not text or anything else)
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
            {
                // allow them to continue
                // (without this, the cursor stays a "NO" symbol
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

                foreach (string file in files)
                {
                    if (UltiMp3Tagger.IsExtensionSupported(file))
                    {
                        e.Effect = DragDropEffects.All;
                        break;
                    }
                }
            }
        }

        private void Form1_DragDrop(object sender, DragEventArgs e)
        {
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

            listViewFiles.Items.Clear();

            int idx = 0;

            foreach (string file in files)
            {
                if (UltiMp3Tagger.IsExtensionSupported(file))
                {

                    ListViewItem lvi = new ListViewItem(new string[] {                     
                        String.Empty,
                        (++idx).ToString("00"),
                        Path.GetFileName(file)
                    });

                    listViewFiles.Items.Add(lvi);
                    
                }
            }

            ListViewFilesItemSelected();

            ToggleButtonReorderTrackInfosEnabled();
        }

        private void ToggleButtonTagEnabled()
        {
            int checkedItemsCount = this.listViewFiles.CheckedItems.Count;

            this.buttonTag.Enabled = checkedItemsCount > 0;
        }

        private void listViewFiles_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            int checkedItemsCount = this.listViewFiles.CheckedItems.Count;

            this.labelFilesCheckedCount.Text = checkedItemsCount.ToString();

            TAG_LIST_MODE tagMode = GetTagModeSelected();

            if (tagMode == TAG_LIST_MODE.SELECTION)
            {
                this.listViewTrackInfos.ItemChecked -= listViewTrackInfos_ItemChecked;

                CheckListViewItems(this.listViewFiles);

                this.listViewTrackInfos.ItemChecked += listViewTrackInfos_ItemChecked;

                this.labelTracksCheckedCount.Text = this.listViewTrackInfos.CheckedItems.Count.ToString();
            }

            ToggleButtonTagEnabled();

            ToggleButtonTagListEnabled();

            ToggleButtonRenameFileEnabled();

            ToggleButtonRenameFolderEnabled();
        }

        private void listViewTrackInfos_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            this.labelTracksCheckedCount.Text = this.listViewTrackInfos.CheckedItems.Count.ToString();

            this.listViewFiles.ItemChecked -= this.listViewFiles_ItemChecked;

            CheckListViewItems(this.listViewTrackInfos);

            this.listViewFiles.ItemChecked += this.listViewFiles_ItemChecked;

            this.labelFilesCheckedCount.Text = this.listViewFiles.CheckedItems.Count.ToString();

            ToggleButtonTagListEnabled();

            ToggleButtonTagEnabled();
        }

        private void listViewTrackInfos_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            TAG_LIST_MODE tagMode = GetTagModeSelected();

            int tracksCount = this.listViewFiles.Items.Count;

            bool forceUncheck = false;

            if (e.Index >= tracksCount)
                forceUncheck = true;
            else if (tagMode != TAG_LIST_MODE.SELECTION)
                forceUncheck = true;

            if (forceUncheck)
                e.NewValue = CheckState.Unchecked;
        }

        private void buttonOrderFilesByName_Click(object sender, EventArgs e)
        {
            OrderListViewFilesInfos(MTUtility.GetFilenameByLevenshteinDistance);
        }

        private void OrderListViewFilesInfos(Func<string[], TrackInfo, string> func)
        {
            List<TrackInfo> list = (from lvi in listViewTrackInfos.Items.Cast<ListViewItem>()
                                    select (TrackInfo)lvi.Tag).ToList();

            string[] files = (from file in listViewFiles.Items.Cast<ListViewItem>()
                              select (Path.GetFileName(((ListViewItem.ListViewSubItem)file.SubItems[ColName]).Text))).ToArray();

            int indexTrack = 0;

            foreach (TrackInfo track in list)
            {
                if (indexTrack > (files.Length - 1))
                    break;

                string fileFound = func(files, track);

                int newindex = Array.IndexOf(files, fileFound);

                if (newindex > -1)
                {
                    string temp = files[newindex]; // temp = "two"
                    files[newindex] = files[indexTrack]; // items[1] = "three"
                    files[indexTrack] = temp; // items[2] = "two"                                            
                }

                indexTrack++;
            }
            

            int idx = 0;

            listViewFiles.Items.Clear();

            foreach (string file in files)
            {
                ListViewItem lvi = new ListViewItem(new[]
                    {
                        String.Empty,
                        (++idx).ToString("00"),
                        Path.GetFileName(file)
                    });

                listViewFiles.Items.Add(lvi);
            }

            //FillListViewTracks(list);   
        }

        private void listViewFiles_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            //int tracksCount = this.listViewTrackInfos.Items.Count;

            //if (e.Index >= tracksCount)
            //    e.NewValue = CheckState.Unchecked;
        }

        private void buttonOrderFilesByPosition_Click(object sender, EventArgs e)
        {
            OrderListViewFilesInfos(MTUtility.GetFilenameByPosition);
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.labelSuggestedStep.Text = String.Empty;
        }

        private void buttonRenameFolder_Click_1(object sender, EventArgs e)
        {
            RenameFolder();
        }

        private void buttonRenameFiles_Click_1(object sender, EventArgs e)
        {
            RenameCheckedFiles();
        }

        private void checkBoxLockPanel_CheckedChanged(object sender, EventArgs e)
        {
            ListViewFilesItemSelected();
        }
    }
}
